/*
============================================================================
 Name        : ThreadSocket.cpp
 Author      : 
 Version     :
 Copyright   : 
 Description : CThreadSocket implementation
============================================================================
*/

#include "ThreadSocket.h"
#include <avkon.hrh>
#include <aknnotewrappers.h>
#include <uri8.h>
#include <http.h>
#include <chttpformencoder.h>
#include <HttpStringConstants.h>
#include <http\RHTTPTransaction.h>
#include <http\RHTTPSession.h>
#include <http\RHTTPHeaders.h>

#include <COMMDB.H> 		//Communications database 
#include <CDBPREFTABLE.H>	//Connection Preferences table
#include <CommDbConnPref.h>

#include <commdbconnpref.h>     //access point
#include <commdb.h>
#include <cdbpreftable.h>
#include <es_enum.h>			// TConnectionInfoV2Buf
#include "ThreadSocket.h"

#include "Global.h"
#include "eStockappui.h"
#include "Logger.h"
#include <eStock_L2hy.rsg>
#include "GprsTools.h"

// Used user agent for requests
_LIT8(KUserAgent, "Nokia");

// This client accepts all content types.
// (change to e.g. "text/plain" for plain text only)
_LIT8(KAccept, "*/*");

//_LIT8(KPostContentTypeStream, "application/octet-stream");
//_LIT8(KPostContentTypeForm, "application/x-www-form-urlencoded");

_LIT8(KConnection,"Keep-Alive");
_LIT8(KRangeBytes, "Range");
_LIT8(KRangeBytesValue, "Range: ");

const TInt KStatustextBufferSize = 512;
const TInt KInfotextBufferSize = 64;
const TInt KURIBufferSize = 128;
const TInt KDefaultBufferSize = 256;

// ----------------------------------------------------------------------------
// CThreadSocket::NewL()
//
// Creates instance of CThreadSocket.
// ----------------------------------------------------------------------------
CThreadSocket* CThreadSocket::NewL()
{
	CThreadSocket* self = CThreadSocket::NewLC();
	CleanupStack::Pop(self);
	return self;
}


// ----------------------------------------------------------------------------
// CThreadSocket::NewLC()
//
// Creates instance of CThreadSocket.
// ----------------------------------------------------------------------------
CThreadSocket* CThreadSocket::NewLC()
{
	CThreadSocket* self = new (ELeave) CThreadSocket();
	CleanupStack::PushL(self);
	self->ConstructL();
	return self;
}


// ----------------------------------------------------------------------------
// CThreadSocket::CThreadSocket()
//
// First phase constructor.
// ----------------------------------------------------------------------------
CThreadSocket::CThreadSocket():
iPostData(NULL),
iRunning(EFalse)
{
}


// ----------------------------------------------------------------------------
// CThreadSocket::~CThreadSocket()
//
// Destructor.
// ----------------------------------------------------------------------------
CThreadSocket::~CThreadSocket()
{	
	iSession.Close();

	delete iPostData;

	ResetData();
}


// ----------------------------------------------------------------------------
// CThreadSocket::ConstructL()
//
// Second phase construction.
// ----------------------------------------------------------------------------
void CThreadSocket::ConstructL()
{
 	iObserver = NULL;
	// Open RHTTPSession with default protocol ("HTTP/TCP")
	TRAPD(err, iSession.OpenL());
	if(err != KErrNone) 
	{
		LOGARG("RHTTPSession OpenL err = %d",err);
		// Most common error; no access point configured, and session creation
		// leaves with KErrNotFound.
		_LIT(KErrMsg,
			"Cannot create session. Is internet access point configured?");
		_LIT(KExitingApp, "Exiting app.");
		CEikonEnv::Static()->InfoWinL(KErrMsg, KExitingApp);
		User::Leave(err);
	}

	iBoolWapConfirmed = EFalse;

	m_gprsTools = CGlobal::Static()->g_GprsTool;
}


// ----------------------------------------------------------------------------
// CThreadSocket::SetHeaderL()
//
// Used to set header value to HTTP request
// ----------------------------------------------------------------------------
void CThreadSocket::SetHeaderL(RHTTPHeaders aHeaders,
							   TInt aHdrField,
							   const TDesC8& aHdrValue)
{
	RStringF valStr = iSession.StringPool().OpenFStringL(aHdrValue);
	CleanupClosePushL(valStr);
	THTTPHdrVal val(valStr);
	aHeaders.SetFieldL(iSession.StringPool().StringF(aHdrField,
		RHTTPSession::GetTable()), val);
	CleanupStack::PopAndDestroy(); // valStr
}

// ----------------------------------------------------------------------------
// CThreadSocket::IssueHTTPPostL()
//
// Start a new HTTP POST transaction.
// ----------------------------------------------------------------------------
void CThreadSocket::IssueHTTPGetL(const TDesC8& aUri,  MClientObserver*	aObserver, TInt nCurSize, TInt nMaxSize)
{
	iObserver = aObserver;
	
	ResetData();
	
	if (!FindExistingConnection())
	{
		CGlobal::Static()->g_bConnectionSetupDone = EFalse;
	}
	
	if(!SetupConnectionL())
	{
		return;
	}
	
	TUriParser8 uri;
	uri.Parse(aUri);
	RStringF method = iSession.StringPool().StringF(
									HTTP::EGET, RHTTPSession::GetTable());

	iTransaction = iSession.OpenTransactionL(uri, *this, method);

	RHTTPHeaders hdr = iTransaction.Request().GetHeaderCollection();
	SetHeaderL(hdr, HTTP::EUserAgent, KUserAgent);
	SetHeaderL(hdr, HTTP::EAccept, KAccept);
	
	if(nCurSize == 0 && nMaxSize == 0)
	{
		SetHeaderL(hdr, HTTP::ERange, KRangeBytes);
	}
	else if(nCurSize >= 0 && nCurSize < nMaxSize)
	{
		TBuf8<64> rangeValue;
		rangeValue.Zero();
		rangeValue.Format(KRangeBytesValue(), nCurSize, nMaxSize);
		SetHeaderL(hdr, HTTP::ERange, rangeValue);
	}

	iTransaction.SubmitL();
	
	if(iObserver != NULL)
		iObserver->ClientUpdateProgress(10);
	iRunning = ETrue;
}

// ----------------------------------------------------------------------------
// CThreadSocket::IssueHTTPPostL()
//
// Start a new HTTP POST transaction.
// ----------------------------------------------------------------------------
void CThreadSocket::IssueHTTPPostL(const TDesC8& aUri,
								   const TDesC8& aBody,
								   MClientObserver*	aObserver,
								   const TDesC8& aContentType)
{
	iObserver = aObserver;
	
	ResetData();

	if (!FindExistingConnection())
	{
		CGlobal::Static()->g_bConnectionSetupDone = EFalse;
	}

	if(!SetupConnectionL())
	{
		return;
	}

	// Parse string to URI
	TUriParser8 uri;
	uri.Parse(aUri);

	// Copy data to be posted into member variable; iPostData is used later in
	// methods inherited from MHTTPDataSupplier.
	delete iPostData;
	iPostData = aBody.AllocL();

	// Get request method string for HTTP POST
	RStringF method = iSession.StringPool().StringF(HTTP::EPOST,
		RHTTPSession::GetTable());

	// Open transaction with previous method and parsed uri. This class will
	// receive transaction events in MHFRunL and MHFRunError.
	TRAPD(err,iTransaction = iSession.OpenTransactionL(uri, *this, method));
	LOGARG("err in OpenTransactionL = %d",err);
	// Set headers for request; user agent, accepted content type and body's
	// content type.
	RHTTPHeaders hdr = iTransaction.Request().GetHeaderCollection();
	SetHeaderL(hdr, HTTP::EUserAgent, KUserAgent);
	SetHeaderL(hdr, HTTP::EAccept, KAccept);
	SetHeaderL(hdr, HTTP::EContentType, aContentType);
	SetHeaderL(hdr, HTTP::EConnection, KConnection);
	 
	TInt n = iPostData->Length();
	TBuf8<10> buf;
	buf.AppendNum(n);
	SetHeaderL(hdr, HTTP::EContentLength,buf);
	// Set this class as an data supplier. Inherited MHTTPDataSupplier methods
	// are called when framework needs to send body data.
	MHTTPDataSupplier* dataSupplier = this;
	iTransaction.Request().SetBody(*dataSupplier);

	// Submit the transaction. After this the framework will give transaction
	// events via MHFRunL and MHFRunError.
	LOGCALL(iTransaction.SubmitL());
	
	if(iObserver != NULL)
		iObserver->ClientUpdateProgress(10);
	iRunning = ETrue;
}

TBool CThreadSocket::FindExistingConnection()
{
	TConnectionInfoBuf connInfo;
	TUint count;
	if (m_gprsTools->iConnection.EnumerateConnections(count) == KErrNone)
	{
		for (TInt i = 1; i <= count; ++i)
		{
			if (m_gprsTools->iConnection.GetConnectionInfo(i, connInfo) == KErrNone)
			{
				if (connInfo().iIapId == CGlobal::Static()->g_AccessPoint.iIap)
					return ETrue;
			}
		}
	}
	return EFalse;
}

// ----------------------------------------------------------------------------
// CThreadSocket::CancelTransaction()
//
// Cancels currently running transaction and frees resources related to it.
// ----------------------------------------------------------------------------
void CThreadSocket::CancelTransaction()
{
	if(!iRunning)
		return;

	// Close() also cancels transaction (Cancel() can also be used but
	// resources allocated by transaction must be still freed with Close())
	iTransaction.Close();
	iObserver = NULL;
	// Not running anymore
	iRunning = EFalse;
}


// ----------------------------------------------------------------------------
// CThreadSocket::MHFRunL()
//
// Inherited from MHTTPTransactionCallback
// Called by framework to pass transaction events.
// ----------------------------------------------------------------------------
void CThreadSocket::MHFRunL(RHTTPTransaction aTransaction,
							const THTTPEvent& aEvent)
{
	LOGARG("MHFRunL status = %d",aEvent.iStatus);
	switch (aEvent.iStatus)
	{
	case THTTPEvent::EGotResponseHeaders:
		{
			// HTTP response headers have been received. Use
			// aTransaction.Response() to get the response. 
			RHTTPResponse resp = aTransaction.Response();
			RHTTPHeaders headers = resp.GetHeaderCollection();
			RStringPool string_pool = iSession.StringPool();
			RStringF contLength = string_pool.StringF(HTTP::EContentLength, RHTTPSession::GetTable());
		
			THTTPHdrVal tempHdrVal;
			TInt err = 0;

			if(!iBoolWapConfirmed)
			{
				RStringF conttype = string_pool.StringF(HTTP::EContentType, RHTTPSession::GetTable());
				err = headers.GetField(conttype,0,tempHdrVal);
				if(err == KErrNone)
				{
					TPtrC8 ptr(tempHdrVal.StrF().DesC());
					LOGDES8(ptr);
					if(ptr.CompareF(_L8("text/vnd.wap.wml")) == 0)
					{
						iBoolWapConfirmed = ETrue;
						aTransaction.Close();
						iRunning = EFalse;
						iObserver->ConnectionOpened();
						break;
					}
				}
			}

			err = headers.GetField(contLength,0,tempHdrVal);
			if(err == KErrNone)
			{
				iRemoteFileSize = tempHdrVal.Int();
				LOGARG("Pkg total size = %d",iRemoteFileSize);
			}
			else
			{
				iRemoteFileSize = 100;
			}
		}
		break;

	case THTTPEvent::EGotResponseBodyData:
		{
			// Part (or all) of response's body data received. Use
			// aTransaction.Response().Body()->GetNextDataPart() to get the actual
			// body data.

			// Get the body data supplier
			MHTTPDataSupplier* body = aTransaction.Response().Body();
			TPtrC8 dataChunk;

			// GetNextDataPart() returns ETrue, if the received part is the last
			// one.
			TBool isLast = body->GetNextDataPart(dataChunk);

			LOGARG("Body Data Received,Length = %d",dataChunk.Length());

			WriteDataL(dataChunk);
			// Always remember to release the body data.
			body->ReleaseData();
		}
		break;

	case THTTPEvent::EResponseComplete:
		{
			// Indicates that header & body of response is completely received.
			// No further action here needed.
			//_LIT(KTransactionComplete, "Transaction Complete");
			//iObserver->ClientEvent(KTransactionComplete);
		}
		break;

	case THTTPEvent::ESucceeded:
		{
			iBoolWapConfirmed = ETrue;
			aTransaction.Close();
			iRunning = EFalse;

			if(iObserver)
				iObserver->ClientBodyReceived(iReceivedData->Des());
			// Indicates that transaction succeeded.
			//_LIT(KTransactionSuccessful, "Transaction Successful");
			//iObserver->ClientEvent(KTransactionSuccessful);

			// Transaction can be closed now. It's not needed anymore.
		}
		break;

	case THTTPEvent::EFailed:
		{
			// Transaction completed with failure
			LOGDES8(iReceivedData->Des());
			aTransaction.Close();
			iRunning = EFalse;
		
			/*TBuf<40> buf;
			CGlobal::GetDesFromID(buf,LOC_BUF_STR_CONNECTERROR);
			if(iObserver != NULL)
				iObserver->ClientEvent(buf);*/
			if(iObserver)
			{
				iObserver->ConnectionOpened();
			}
		}
		break;

	default:
		// There are more events in THTTPEvent, but they are not usually
		// needed. However, event status smaller than zero should be handled
		// correctly since it's error.
		{
			if (aEvent.iStatus < 0)
			{
				aTransaction.Close();
				iRunning = EFalse;
			} 
		}
		break;
	}
}


// ----------------------------------------------------------------------------
// CThreadSocket::MHFRunError()
//
// Inherited from MHTTPTransactionCallback
// Called by framework when *leave* occurs in handling of transaction event.
// These errors must be handled, or otherwise HTTP-CORE 6 panic is thrown.
// ----------------------------------------------------------------------------
TInt CThreadSocket::MHFRunError(TInt aError,
								RHTTPTransaction /*aTransaction*/,
								const THTTPEvent& /*aEvent*/)
{

	LOGARG("MHFRunError aError = %d",aError);
	// Just notify about the error and return KErrNone.
	TBuf<KInfotextBufferSize>	text;
	_LIT(KRunError, "MHFRunError: %d");
	text.Format(KRunError, aError);
	LOGDES16(text);
	if(iObserver != NULL)
	iObserver->ClientEvent(text);
	return KErrNone;
}


// ----------------------------------------------------------------------------
// CThreadSocket::GetNextDataPart()
//
// Inherited from MHTTPDataSupplier
// Called by framework when next part of the body is needed. In this
// this provides data for HTTP post.
// ----------------------------------------------------------------------------
TBool CThreadSocket::GetNextDataPart(TPtrC8& aDataPart)
{
	if(iPostData)
	{
		// Provide pointer to next chunk of data (return ETrue, if last chunk)
		// Usually only one chunk is needed, but sending big file could require
		// loading the file in small parts.
		aDataPart.Set(iPostData->Des());
	}
	return ETrue;
}


// ----------------------------------------------------------------------------
// CThreadSocket::ReleaseData()
//
// Inherited from MHTTPDataSupplier
// Called by framework. Allows us to release resources needed for previous
// chunk. (e.g. free buffers)
// ----------------------------------------------------------------------------
void CThreadSocket::ReleaseData()
{
	// It's safe to delete iPostData now.
	delete iPostData;
	iPostData = NULL;
}

// ----------------------------------------------------------------------------
// CThreadSocket::Reset()
//
// Inherited from MHTTPDataSupplier
// Called by framework to reset the data supplier. Indicates to the data
// supplier that it should return to the first part of the data.
// In practise an error has occured while sending data, and framework needs to
// resend data.
// ----------------------------------------------------------------------------
TInt CThreadSocket::Reset()
{
	// Nothing needed since iPostData still exists and contains all the data.
	// (If a file is used and read in small parts we should seek to beginning
	// of file and provide the first chunk again in GetNextDataPart() )
	return KErrNone;
}


// ----------------------------------------------------------------------------
// CThreadSocket::OverallDataSize()
//
// Inherited from MHTTPDataSupplier
// Called by framework. We should return the expected size of data to be sent.
// If it's not know we can return KErrNotFound (it's allowed and does not cause
// problems, since HTTP protocol allows multipart bodys without exact content
// length in header).
// ----------------------------------------------------------------------------
TInt CThreadSocket::OverallDataSize()
{
	if(iPostData)
		return iPostData->Length();
	else
		return KErrNotFound ;
}


// ----------------------------------------------------------------------------
// CThreadSocket::SetupConnectionL()
//
// The method set the internet access point and connection setups.
// ----------------------------------------------------------------------------	
TBool CThreadSocket::SetupConnectionL()
{
	if( iConnectionSetupDone )
		return ETrue;

	if(m_gprsTools->OpenHttpSession(iSession,this))
	{
		iConnectionSetupDone = ETrue;
		return ETrue;
	}
	else
	{
		return EFalse;
	}
}

void CThreadSocket::WriteDataL(const TDesC8& aData)
{
	// some data already received
	if (iReceivedData)
	{
		iReceivedData = iReceivedData->ReAllocL(iReceivedData->Length() + aData.Length());
		iReceivedData->Des().Append(aData);

	}
	// no data yet received.
	else
	{
		iReceivedData = HBufC8::NewL(aData.Length());
		iReceivedData->Des().Copy(aData);
	}
	
	TInt percent =(TInt)((iReceivedData->Length()*100)/iRemoteFileSize);
	if(iObserver != NULL)
	{
		iObserver->ClientUpdateProgress(percent);
	}
	
}

void CThreadSocket::ResetData()
{
	delete iReceivedData;
	iReceivedData = NULL;
}

//#ifdef __SERIES60_3X__
//void CThreadSocket::GetApList(RArray<TIapData>& aApArray)
//{
//	m_gprsTools->GetApList(aApArray);
//}
//#endif
